#include <iostream>
#include <climits>
template <typename T>
struct twoPoint{
  T *p1, *p2;
  twoPoint(T* _p1, T* _p2): p1(_p1), p2(_p2) {}
  ~twoPoint(){
    delete[] p1, p2;
  }
};
struct Table{
  int size;
  int** table;
  Table(int _size): size(_size){
    this->table=new int*[this->size];
    for(int i=0; i<this->size; ++i){
      this->table[i]=new int[this->size];
    }
  }
  ~Table(){
    for(int i=0; i<this->size; ++i) delete[] this->table[i];
    delete[] this->table;
  }
  int* operator[](int n){
    return this->table[n];
  }
};
twoPoint<Table> MatrixChainOrder(int* p, int size){
  int n=size-1;
  Table* m=new Table(n);
  Table* s=new Table(n-1);
  for(int i=0; i<n; ++i) (*m)[i][i]=0;
  for(int l=2; l<=n; ++l){   
    for(int i=0; i<n-l+1; ++i){
      int j=i+l-1;
      (*m)[i][j]=INT_MAX;
      for(int k=i; k<j; ++k){
        int q=(*m)[i][k]+(*m)[k+1][j]+p[i]*p[k+1]*p[j+1];
        if(q<(*m)[i][j]){
          (*m)[i][j]=q;
          (*s)[i][j-1]=k+1;
        }
      }
    }
  }
  return twoPoint<Table>(m, s);
}
void PrintOptimalParens(Table* s, int i, int j){
  if(i==j) std::cout<<"A"<<i;
  else{
    std::cout<<"(";
    PrintOptimalParens(s, i, (*s)[i-1][j-2]);
    PrintOptimalParens(s, (*s)[i-1][j-2]+1, j);
    std::cout<<")";
  }
}
int main(void){
  int p[]={30, 35, 15, 5, 10, 20, 25};
  int size=sizeof(p)/ sizeof(int);
  twoPoint<Table> res=MatrixChainOrder(p, size);
  PrintOptimalParens(res.p2, 1, 6);
  return 0;
}